package com.snoob.springboot.util;

import com.snoob.springboot.base.BaseException;

import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @描述: TODO 文件工具类
 * @作者: lixing lixing_java@163.com
 * @日期 2019/10/10 21:51
 */
public class FileUtil {
    /**
     * @描述 TODO 创建文件夹
     * @作者 lixing
     * @日期 2019/7/6 13:40
     * @Param [directoryPath 文件夹的绝对路径]
     */
    public static Path createDirectory(String directoryPath) {
        Path sourcePath = Paths.get(directoryPath);
        if (!Files.exists(sourcePath)) {
            try {
                sourcePath = Files.createDirectories(sourcePath);
            } catch (IOException e) {
                LoggerUtil.error(MessageFormat.format("创建文件夹出错：{0}", e.getMessage()));
                throw new BaseException("创建文件夹出错");
            }
        }
        return sourcePath;
    }

    /**
     * @描述 TODO 创建文件
     * @作者 lixing
     * @日期 2019/7/6 13:43
     * @Param [filePath 文件的绝对路径]
     */
    public static void createFile(String filePath) {
        Path path = Paths.get(filePath);
        if (!Files.exists(path)) {
            try {
                Files.createFile(path);
            } catch (IOException e) {
                LoggerUtil.error(MessageFormat.format("创建文件出错：{0}", e.getMessage()));
                throw new BaseException("创建文件出错");
            }
        }
    }

    /**
     * @描述 TODO 删除文件、文件夹
     * @作者 lixing
     * @日期 2019/7/6 13:45
     * @Param [path 文件、文件夹的绝对路径]
     */
    public static void deleteFile(String path) {
        try {
            Files.deleteIfExists(Paths.get(path));
        } catch (IOException e) {
            LoggerUtil.error(MessageFormat.format("删除文件、文件夹出错：{0}", e.getMessage()));
            throw new BaseException("删除文件、文件夹出错");
        }
    }

    /**
     * @return java.util.Map<java.lang.String, java.lang.String> name文件名称 type文件类型 relativePath相对路径
     * @描述 TODO 获取绝对路径中的相对路径、文件名称、后缀
     * @作者 lixing
     * @日期 2020/1/3 21:01
     * @Param [absolutePath]
     */
    public static Map<String, String> getFileNameFromAbsolutePath(String absolutePath) {
        if (StrUtil.isEffective(absolutePath)) {
            Map<String, String> stringMap = new HashMap<>();

            // 从文件路径的尾部搜索字符 "\" 的索引
            int index = absolutePath.lastIndexOf("\\");
            String fileName = absolutePath.substring(index + 1).toLowerCase(); // 文件名称
            stringMap.put("name", fileName);

            index = fileName.lastIndexOf(".");
            String fileType = fileName.substring(index + 1).toLowerCase(); // 扩展名
            stringMap.put("type", fileType);

            String relativePath = absolutePath.replace(fileName, ""); // 相对路径
            stringMap.put("relativePath", relativePath);

            return stringMap;
        } else {
            throw new BaseException("文件路径地址为空");
        }
    }

    /**
     * @描述 TODO 本地文件拷贝
     * @作者 lixing
     * @日期 2019/7/6 14:00
     * @Param [sourceFilePath 源文件绝对路径, targetDirectoryPath 目标目录绝对路径, targetFileName 目标文件名称]
     */
    public static void copyFile(String sourceFilePath, String targetDirectoryPath, String targetFileName) {
        if (StrUtil.isEffective(sourceFilePath) && Files.exists(Paths.get(sourceFilePath))) {
            Path targetDirectoryPath_ = Paths.get(targetDirectoryPath);
            if (!Files.exists(targetDirectoryPath_) || !Files.isDirectory(targetDirectoryPath_)) {
                createDirectory(targetDirectoryPath); // 创建目标目录
            }
            if (!StrUtil.isEffective(targetFileName)) {
                targetFileName = getFileNameFromAbsolutePath(sourceFilePath).get("name"); // 从源文件的绝对路径中获取文件名
            }
            try {
                String targetFilePath; // 目标文件的绝对路径
                if (targetDirectoryPath.endsWith("\\")) {
                    targetFilePath = targetDirectoryPath + targetFileName;
                } else {
                    targetFilePath = targetDirectoryPath + File.separator + targetFileName;
                }
                Files.copy(Paths.get(sourceFilePath), Paths.get(targetFilePath));
            } catch (IOException e) {
                LoggerUtil.error(MessageFormat.format("拷贝文件出错：{0}", e.getMessage()));
                throw new BaseException("拷贝文件出错");
            }
        } else {
            throw new BaseException(MessageFormat.format("被拷贝文件：{0} 不存在", targetFileName));
        }
    }

    /**
     * @描述 TODO 本地文件拷贝到输出流【导出excel到浏览器】
     * @作者 lixing
     * @日期 2019/7/6 15:31
     * @Param [source 源文件绝对路径, outputStream 字节输出流]
     */
    public static void copyFile(String sourceFilePath, OutputStream outputStream) {
        if (StrUtil.isEffective(sourceFilePath) && Files.exists(Paths.get(sourceFilePath))) {
            if (outputStream != null) {
                Path sourceFilePath_ = Paths.get(sourceFilePath);
                try {
                    Files.copy(sourceFilePath_, outputStream);
                } catch (IOException e) {
                    LoggerUtil.error(MessageFormat.format("拷贝文件出错：{0}", e.getMessage()));
                    throw new BaseException("拷贝文件出错");
                }
            } else {
                throw new BaseException("outputStream对象为空，无法拷贝");
            }
        } else {
            throw new BaseException(MessageFormat.format("被拷贝文件：{0} 不存在", sourceFilePath));
        }
    }

    /**
     * @描述 TODO 输入流拷贝到本地文件【下载文件并保存到本地】
     * @作者 lixing
     * @日期 2020/1/3 11:28
     * @Param [inputStream 字节输入流, targetDirectoryPath 目标目录绝对路径, targetFileName 目标文件名称]
     */
    public static void copyFile(InputStream inputStream, String targetDirectoryPath, String targetFileName) {
        if (inputStream != null) {
            if (StrUtil.isEffective(targetDirectoryPath)) {
                createDirectory(targetDirectoryPath);
                try {
                    String targetFilePath; // 目标文件的绝对路径
                    if (targetDirectoryPath.endsWith("\\")) {
                        targetFilePath = targetDirectoryPath + targetFileName;
                    } else {
                        targetFilePath = targetDirectoryPath + File.separator + targetFileName;
                    }
                    Files.copy(inputStream, Paths.get(targetFilePath));
                } catch (IOException e) {
                    LoggerUtil.error(MessageFormat.format("拷贝文件出错：{0}", e.getMessage()));
                    throw new BaseException("拷贝文件出错");
                }
            } else {
                throw new BaseException("absolutePath地址为空，无法拷贝");
            }
        } else {
            throw new BaseException("inputStream对象为空，无法拷贝");
        }
    }

    /**
     * @描述 TODO 将文件（网络文件、本地文件）转化成InputStream流
     * @作者 lixing
     * @日期 2020/1/3 11:09
     * @Param [fileUrl 文件网络地址]
     */
    public static InputStream convertFileToInputStream(String fileUrl) throws IOException {
        InputStream inputStream = null;
        if (fileUrl.startsWith("https")) {
            URL url = new URL(fileUrl);
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
            //
            httpsURLConnection.setRequestMethod("GET");
            httpsURLConnection.setConnectTimeout(60 * 1000);
            httpsURLConnection.setReadTimeout(60 * 1000);
            //
            httpsURLConnection.setRequestProperty("Charset", "UTF-8");
            httpsURLConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            httpsURLConnection.setRequestProperty("Connection", "Keep-Alive");
            httpsURLConnection.setRequestProperty("logType", "base");
            //
            inputStream = httpsURLConnection.getInputStream();
        } else if (fileUrl.startsWith("http")) {
            URL url = new URL(fileUrl);
            URLConnection urlConnection = url.openConnection();
            urlConnection.setConnectTimeout(60 * 1000);
            urlConnection.setReadTimeout(5 * 1000);
            inputStream = urlConnection.getInputStream();
        } else {
            inputStream = new FileInputStream(new File(fileUrl));
        }
        return inputStream;
    }

    /**
     * @描述 TODO 将InputStream转化成字节数组
     * @作者 lixing
     * @日期 2020/1/3 11:09
     * @Param [byte数组]
     */
    public static byte[] convertInputStreamToByteArray(InputStream inputStream) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inputStream.read(buffer)) != -1) {
            byteArrayOutputStream.write(buffer, 0, len);
        }
        inputStream.close();
        return byteArrayOutputStream.toByteArray();
    }

    /**
     * @return boolean
     * @描述 TODO 输出InputStream流到浏览器
     * @作者 lixing
     * @日期 2020/1/3 11:09
     * @Param [fileUrl 文件网络地址]
     */
    public static void downFile(InputStream inputStream, String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            String userAgent = request.getHeader("USER-AGENT");
            if (userAgent.contains("Edge")) {
                fileName = URLEncoder.encode(fileName, "UTF8");
            } else if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
                // IE浏览器
                fileName = URLEncoder.encode(fileName, "UTF8");
            } else if (userAgent.contains("Mozilla")) {
                // google,火狐浏览器
                fileName = new String(fileName.getBytes(), "ISO8859-1");
            } else {
                //其他浏览器
                fileName = URLEncoder.encode(fileName, "UTF8");
            }
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            bufferedInputStream = new BufferedInputStream(inputStream);
            bufferedOutputStream = new BufferedOutputStream(response.getOutputStream());
            int length = 0;
            byte[] temp = new byte[1024 * 10];
            while ((length = bufferedInputStream.read(temp)) != -1) {
                bufferedOutputStream.write(temp, 0, length);
            }
            bufferedOutputStream.flush();
        } catch (IOException e) {
            throw new BaseException("将服务器文件输出到浏览器出错：" + e.getMessage());
        } finally {
            if (bufferedOutputStream != null) {
                try {
                    bufferedOutputStream.close();
                } catch (IOException ioe) {
                    //new BaseResultT<String>().fail(MessageFormat.format("将服务器文件输出到浏览器，bufferedOutputStream.close()出错：{0}", ioe.getMessage()));
                    ioe.fillInStackTrace();
                }
            }
            //
            if (bufferedInputStream != null) {
                try {
                    bufferedInputStream.close();
                } catch (IOException ioe) {
                    //BaseResult.fail(MessageFormat.format("将服务器文件输出到浏览器，bufferedInputStream.close()出错：{0}", ioe.getMessage()));
                    ioe.fillInStackTrace();
                }
            }
            //
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException ioe) {
                    //BaseResult.fail(MessageFormat.format("将服务器文件输出到浏览器，inputStream.close()出错：{0}", ioe.getMessage()));
                    ioe.fillInStackTrace();
                }
            }
        }
    }

    /**
     * @return DirectoryStream<Path>
     * @描述 TODO 遍历文件夹
     * @作者 lixing
     * @日期 2019/7/7 20:12
     * @Param [source 目标文件夹]
     */
    public DirectoryStream<Path> ergodicDirectory(String source) {
        Path path = Paths.get(source);
        if (Files.exists(path) || Files.isDirectory(path)) {
            try {
                return Files.newDirectoryStream(path);
//                for (Path p : pathDirectoryStream) {
//                    System.out.println(p.getFileName());
//                }
            } catch (IOException e) {
                LoggerUtil.error(MessageFormat.format("遍历文件夹时出错：{0}", e.getMessage()));
                throw new BaseException("遍历文件夹时出错");
            }
        } else {
            LoggerUtil.error("该文件不存在或不是一个文件夹");
            throw new BaseException("该文件不存在或不是一个文件夹");
        }
    }

    /**
     * @描述 TODO 单行文本写入文本文件
     * @作者 lixing
     * @日期 2019/7/6 17:40
     * @Param [text, target]
     */
    public void writerText(String text, String target) {
        Path path = Paths.get(target);
        if (Files.exists(path)) {
            try {
                Files.write(path, text.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND); // 追加
            } catch (IOException e) {
                LoggerUtil.error(MessageFormat.format("writerText写入数据到文本文件出错：{0}", e.getMessage()));
                throw new BaseException("写入数据到文本文件出错");
            }
        } else {
            throw new BaseException("目标文件文件不存在");
        }
    }

    /**
     * @描述 TODO 多行文本写入文本文件
     * @作者 lixing
     * @日期 2019/7/6 16:50
     * @Param [textData 待写入的文本数据, source 目标文件]
     */
    public void writerText(List<String> texts, String target) {
        Path path = Paths.get(target);
        if (Files.exists(path)) {
            try {
                Files.write(path, texts, StandardOpenOption.APPEND); // 追加
            } catch (IOException e) {
                LoggerUtil.error(MessageFormat.format("writerText批量写入数据到文本文件出错：{0}", e.getMessage()));
                throw new BaseException("批量写入数据到文本文件出错");
            }
        } else {
            throw new BaseException("目标文件文件不存在");
        }
    }

    /**
     * @return java.lang.String
     * @描述 TODO 读取文本文件数据
     * @作者 lixing
     * @日期 2019/7/6 16:24
     * @Param [filePath]
     */
    public List<String> readText(String source) {
        try {
            return Files.readAllLines(Paths.get(source), StandardCharsets.UTF_8);
        } catch (IOException e) {
            LoggerUtil.error(MessageFormat.format("readText读取文本文件数据出错：{0}", e.getMessage()));
            throw new BaseException("读取文本文件数据");
        }
    }

}
